{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modSondageList;

interface

uses Classes, SysUtils,
     modBaseType,
     ESBDates,
     sFonctionsChaines;


type
    TPlaneteASonder = record
        // coordonnées à sonder
        Coord   : TPlanetLocation;

        // nombre de sonde à envoyer
        NbSondes: integer;

        // -1 si pas encore sondé
        DateSondage: int64;

        // Coordonnées d'envoi des sondes
        Source: TPlanetLocation;


        // Evaluation des ressources en cas d'attaque de sondes
        //RessourceEval: integer;

        // Définit si c'est une planète qui a été pushé en tête de liste suite
        // à l'utilisation de deux slots de flotte
        //bIsPushed: boolean;
    end;

    PPlaneteASonder = ^TPlaneteASonder;
    TCollectionPlaneteASonder = class(TList)
    private
       {--------------------------------------------------}
       {Elements spécialisés de la classes}


      
       {--------------------------------------------------}
       {Gestion des pointeurs}
       // Utilisé pour la propriété Itemx
       function GetItemx(Index: integer): TPlaneteASonder;
    public
       function GetNextASonder: PPlaneteASonder;
       function AddIfNotExist(Element: TPlaneteASonder): PPlaneteASonder;
       function AddIfNotExistCoord(Element: TPlanetLocation): PPlaneteASonder;


       // Réorganise les planètes à sonder de telle sorte que celles qui sont
       // proche de la source soit sondé en premier
       procedure ReorganiseFromSource(Src: TPlanetLocation; bCommenceAGauche: boolean);
       procedure ReorganiseFromSourceRnd(Src: TPlanetLocation);

       {--------------------------------------------------}
       {Gestion des pointeurs}
       // *** Ajout de pointeurs
       // Renvoi le pointeur de l'élément ajouté
       function Add(Element: TPlaneteASonder): PPlaneteASonder;

       // Simple ajout de pointeur
       procedure SimpleAdd(Pointeur: PPlaneteASonder);
      
       // Renvoi le pointeur de l'élément ajouté/modifié
       // Utilise Get_Index pour vérifier si l'élément doit être mis à jour
       function Add_Update(Element: TPlaneteASonder): PPlaneteASonder;
      
      
       // *** Suppression de pointeur
       // Libère et supprime le pointeur Index
       // Renvoi True si réussit, False si hors limite ou erreur
       function Delete(Element: TPlanetLocation): boolean; overload;
       function Delete(Index: integer): boolean; overload;

      
       // *** Effacement de la liste de pointeur
       procedure Clear; override;
       function ClearNonSondee: integer;
       function ClearDejaSondee: integer;

       // *** Requete sur la liste de pointeur
       function Get_Index(Element: TPlanetLocation): integer;
       function Get_Pointeur(Element: TPlanetLocation): PPlaneteASonder;
       function Get_Item(Index: integer): TPlaneteASonder;
       property Itemx[Index: integer]: TPlaneteASonder read GetItemx;


       procedure ResetDateToZero;

       {--------------------------------------------------}
       {Procédures et fonctions spécialisées de la classes}
       // Destructeur
       destructor Destroy; override;
       constructor Create;

    end;



    function trieSondageFromSourceStartADroite(e1, e2: Pointer): integer;
    function trieSondageFromSourceStartAGauche(e1, e2: Pointer): integer;

    
implementation


var
   FQSTrieSource: TPlanetLocation;


{*******************************************************************************
 *******************************************************************************
 **               TCollectionPlaneteASonder                                     **
 *******************************************************************************
 *******************************************************************************}


function trieSondageFromSourceStartAGauche(e1, e2: Pointer): integer;
var l1, l2: TPlanetLocation;
begin

    l1 := PPlaneteASonder(e1).Coord;
    l2 := PPlaneteASonder(e2).Coord;

    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    // Voir http://alphomega.developpez.com/TriTList/
    // pour savoir comment trier sur plusieurs critére

    Result := 0;

    if l1.SystemSolaire = l2.SystemSolaire then begin
       // meme système solaire
       exit;
    end;


    if l1.SystemSolaire = FQSTrieSource.SystemSolaire then begin

       if l2.SystemSolaire < l1.SystemSolaire then begin
          // l2  l1=src
          // l2 est après l1
          Result := -1;
       end else begin
          // l1=src l2
          // l2 est après l1
          Result := -1;
       end;
       exit;

    end else if l2.SystemSolaire = FQSTrieSource.SystemSolaire then begin

       if l1.SystemSolaire < l2.SystemSolaire then begin
          // l1 l2=src
          // l1 est après l2
          Result := 1;
       end else begin
          // l2=src l1
          Result := 1;
       end;
       exit;
    end;


    // si on arrive ici, nous avons:
    // l1 <> l2
    // l1 <> src
    // l2 <> src

    if l1.SystemSolaire < l2.SystemSolaire then begin

       if l2.SystemSolaire <= FQSTrieSource.SystemSolaire then begin
          // tous les deux à gauche de la source
          //   l1  l2  src
          // MAIS il faut commencer par celui le plus proche de src, c'est
          // à dire ici l2, donc l1 > l2
          Result := 1;

       end else if l1.SystemSolaire >= FQSTrieSource.SystemSolaire then begin
          // tous les deux à droite de la source
          //   src  l1  l2
          // le plus proche de src: l1
          Result := -1;

       end else begin
          // l1 est à gauche, et l2 est à droite...
          // l1  src  l2
          // on commence par la gauche, donc 1l passe avant l2
          Result := -1;
       end;

       exit;

    end else begin
       // sinon l2 < l1

       if l1.SystemSolaire <= FQSTrieSource.SystemSolaire then begin
          // tous les deux à gauche
          //  l2  l1    src
          // MAIS il faut commencer par celui le plus proche de src, c'est
          // à dire ici l1, donc l1 < l2
          Result := -1;

       end else if l2.SystemSolaire >= FQSTrieSource.SystemSolaire then begin
          // tous les deux à droite
          //   src   l2  l1  
          Result := 1;

       end else begin
          // l1 est à gauche, et l2 est à droite...
          // l2  src  l1
          Result := 1;
       end;

       exit;
    end;


end;


function trieSondageFromSourceStartADroite(e1, e2: Pointer): integer;
var l1, l2: TPlanetLocation;
begin

    l1 := PPlaneteASonder(e1).Coord;
    l2 := PPlaneteASonder(e2).Coord;

    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    // Voir http://alphomega.developpez.com/TriTList/
    // pour savoir comment trier sur plusieurs critére

    Result := 0;

    if l1.SystemSolaire = l2.SystemSolaire then begin
       // meme système solaire
       exit;
    end;


    if l1.SystemSolaire = FQSTrieSource.SystemSolaire then begin

       if l2.SystemSolaire < l1.SystemSolaire then begin
          // l2  l1=src
          // l2 est apres l1
          Result := -1;
       end else begin
          // l1=src  l2
          // l2 est apres l1
          Result := -1;
       end;
       exit;

    end else if l2.SystemSolaire = FQSTrieSource.SystemSolaire then begin
       if l1.SystemSolaire < l2.SystemSolaire then begin
          // l1  l2=src
          // l1 est apres l2
          Result := 1;
       end else begin
          // l2=src l1
          // l1 est apres l2
          Result := 1;
       end;
       exit;
    end;


    // si on arrive ici, nous avons:
    // l1 <> l2
    // l1 <> src
    // l2 <> src

    if l1.SystemSolaire < l2.SystemSolaire then begin

       if l2.SystemSolaire <= FQSTrieSource.SystemSolaire then begin
          // tous les deux à gauche de la source
          //   l1  l2  src
          // MAIS il faut commencer par celui le plus proche de src, c'est
          // à dire ici l2, donc l1 > l2
          Result := 1;

       end else if l1.SystemSolaire >= FQSTrieSource.SystemSolaire then begin
          // tous les deux à droite de la source
          //   src  l1  l2
          // le plus proche de src: l1
          Result := -1;

       end else begin
          // l1 est à gauche, et l2 est à droite...
          // l1  src  l2
          // on commence par la droite, donc 12 passe avant l1
          Result := 1;
       end;

       exit;

    end else begin
       // sinon l2 < l1

       if l1.SystemSolaire <= FQSTrieSource.SystemSolaire then begin
          // tous les deux à gauche
          //  l2  l1    src
          // MAIS il faut commencer par celui le plus proche de src, c'est
          // à dire ici l1, donc l1 < l2
          Result := -1;

       end else if l2.SystemSolaire >= FQSTrieSource.SystemSolaire then begin
          // tous les deux à droite
          //   src   l2  l1  
          Result := 1;

       end else begin
          // l1 est à gauche, et l2 est à droite...
          // l2  src  l1
          Result := -1;
       end;

       exit;
    end;


end;

procedure TCollectionPlaneteASonder.ReorganiseFromSourceRnd(Src: TPlanetLocation);
var bType: boolean;
    id64: int64;
begin

    id64 := DateTimeToUnix(Now);

    id64 := id64 and 1;

    if id64 > 0 then begin
       bType := true;
    end else begin
       bType := false;
    end;

    ReorganiseFromSource(Src, bType);

end;

procedure TCollectionPlaneteASonder.ReorganiseFromSource(Src: TPlanetLocation; bCommenceAGauche: boolean);
begin

    FQSTrieSource := Src;

    if bCommenceAGauche then begin
       Sort(trieSondageFromSourceStartAGauche);
    end else begin
       Sort(trieSondageFromSourceStartADroite);

    end;


end;

function TCollectionPlaneteASonder.GetNextASonder: PPlaneteASonder;
var i: integer;
begin
    if Count = 0 then begin
       Result := nil;
       exit;
    end;

    for i := 0 to Count - 1 do begin
        Result := Items[i];

        if Result.DateSondage < 1 then begin
           exit;
        end;
    end;

    // Tout a déjà été sondé
    Result := nil;

end;


constructor TCollectionPlaneteASonder.Create;
begin
    
    // Héritage
    InHerited Create;
    
    
    
    // Code pour l'initialisation ici

    
end;

destructor TCollectionPlaneteASonder.Destroy;
begin
    // Implémenter le code avant l'héritage de la méthode
    // C'est à dire ici
    
   
    // On appelle NOTRE clear qui lui-même appelle NOTRE Delete qui libère l'espace mémoire
    // avant d'enlever le pointeur de la TList
    // L'héritage de Destroy appelle Clear, mais es-ce le notre ou celui de la classe de base ?
    Clear;
    
    
    // Héritage
    InHerited Destroy;
end;

function TCollectionPlaneteASonder.ClearDejaSondee: integer;
var i: integer;
    pe : PPlaneteASonder;
begin

    Result := 0;

    if Count = 0 then begin
       exit;
    end;

    for i := Count - 1 downto 0 do begin
        pe := Items[i];

        if pe.DateSondage > 2000 then begin
           Dispose(pe);
           inherited Delete(i);

           Inc(Result);
        end;

    end;

end;


function TCollectionPlaneteASonder.ClearNonSondee: integer;
var i: integer;
    pe : PPlaneteASonder;
begin

    Result := 0;

    if Count = 0 then begin
       exit;
    end;

    for i := Count - 1 downto 0 do begin
        pe := Items[i];

        if pe.DateSondage < 20 then begin
           Dispose(pe);
           inherited Delete(i);

           Inc(Result);
        end;

    end;

end;

procedure TCollectionPlaneteASonder.ResetDateToZero;
var i: integer;
    pe : PPlaneteASonder;
begin

    if Count = 0 then begin
       exit;
    end;

    for i := Count - 1 downto 0 do begin
        pe := Items[i];
        pe.DateSondage := -1;
    end;

end;

procedure TCollectionPlaneteASonder.Clear;
var i: integer;
begin

    if Count > 0 then begin
        For i := Count -1 downto 0 do
            Delete(i);
    end;
    
    inherited Clear;
end;


function TCollectionPlaneteASonder.Get_Pointeur(Element: TPlanetLocation): PPlaneteASonder;
var i: integer;
begin

   Result := nil;

   i := Get_Index(Element);
   if i < 0 then exit;
   Result := Items[i];

end;


function TCollectionPlaneteASonder.Get_Index(Element: TPlanetLocation): integer;
var i: integer;
    pElement: PPlaneteASonder;
begin

   Result := -1;

   if Count <= 0 then exit;

   for i := 0 to Count - 1 do begin
      pElement := Items[i];

      {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
      {### Neccessite adpation en fonction du type record  ### }
      {### ### ### ### ### ### ### ### ### ### ### ### ### ### }

      if CompareLocation(pElement^.Coord, Element) then begin
         Result := i;
         exit;
      end;
   end;

end;


function TCollectionPlaneteASonder.AddIfNotExist(Element: TPlaneteASonder): PPlaneteASonder;
var i : integer;
begin

     i := Get_Index(Element.Coord);

     if i < 0 then begin
        // L'élément n'existe pas
        // on l'ajoute
        Result  := Add(Element);
     end else begin
        // L'élément existe déjà
        // On ne le modifie pas
        Result := nil;
     end;

end;

function TCollectionPlaneteASonder.AddIfNotExistCoord(Element: TPlanetLocation): PPlaneteASonder;
var i : integer;
    e: TPlaneteASonder;
begin

     i := Get_Index(Element);

     if i < 0 then begin
        // L'élément n'existe pas
        // on l'ajoute
        e.Coord := Element;
        e.NbSondes := 1;
        e.DateSondage := -1;

        Result := Add(e);


     end else begin
        // L'élément existe déjà
        // On ne le modifie pas
        // renvoi nil pour indiquer que l'élément existe déjà
        Result := nil;
     end;

end;


function TCollectionPlaneteASonder.Add_Update(Element: TPlaneteASonder): PPlaneteASonder;
var i : integer;
begin

     Result := nil;
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     i := Get_Index(Element.Coord);        {### Neccessite adpation en fonction du type record  ### }
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     if i < 0 then begin
        // L'élément n'existe pas, l'ajoute
        Result  := Add(Element);
     end else begin
        // L'élément existe déjà, le met à jour
        Result  := Items[i];     // Obtient le pointeur
        Result^ := Element;
     end;

end;




function TCollectionPlaneteASonder.Get_Item(Index: integer): TPlaneteASonder;
var pElement: PPlaneteASonder;
begin

     if Index < 0 then exit;
     if Index >= Count then exit;

     pElement := Items[Index];     // Obtient le pointeur
     Result := pElement^;

end;

function TCollectionPlaneteASonder.Delete(Index: integer): boolean;
var pElement: PPlaneteASonder;
begin

    Result := false;

    if (Index < 0) or (Index >= Count) then
       // Hors limite
       exit;


    // Obtient le pointeur
    pElement := Items[Index];

    if pElement <> nil then
       // Libére la mémoire créée lors de l'ajout
       Dispose(pElement);

    // Supprime le pointeur de la liste
    inherited Delete(Index);

    // Met le resultat à True
    Result := true;

end;

function TCollectionPlaneteASonder.Delete(Element: TPlanetLocation): boolean;
var i : integer;
begin

    Result := false;
    i      := Get_Index(Element);

    if i >= 0 then begin
       // Element trouvé
       // Le supprime
       Delete(i);

       // Même si la suppression Echoue on renvoi True
       // Car l'élément a été trouvé
       Result := true;
    end;
end;


function TCollectionPlaneteASonder.Add(Element: TPlaneteASonder): PPlaneteASonder;
var pElement: PPlaneteASonder;
begin
    
    Result := nil;
    
    New(pElement);          // Créer l'espace mémoire requis
    pElement^ := Element;   // Recopie les données dans l'emplacement pointé
    
    try
       inherited Add(pElement);  // Ajoute le pointeur à la liste
       Result := pElement;
    except
       Result := nil;
    end;
    
    // Pour libérer la mémoire occupé par l'élément pointer:  Dispose(pElement);

end;

// Simple ajout de pointeur
procedure TCollectionPlaneteASonder.SimpleAdd(Pointeur: PPlaneteASonder);
begin
    
    inherited Add(Pointeur);  // Ajoute le pointeur à la liste
    
end;

function TCollectionPlaneteASonder.GetItemx(Index: integer): TPlaneteASonder;
var pElement: PPlaneteASonder;
begin

   if Count < 0 then exit;
   if Index >= Count then exit;

   pElement := Items[Index];
   Result := pElement^;

end;

end.
